import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"
import { getToken } from "@@/utils/cache/cookies"
import axios from "axios"
import { get, merge } from "lodash-es"
import { useUserStore } from "@/pinia/stores/user"
import { ElMessage } from "element-plus"

// 请求重试配置
interface RetryConfig {
  retries: number
  retryDelay: number
  retryCondition?: (error: any) => boolean
}

// 请求缓存
const requestCache = new Map<string, Promise<any>>()

/** 退出登录并强制刷新页面（会重定向到登录页） */
function logout() {
  useUserStore().logout()
  location.reload()
}

/** 生成请求缓存key */
function generateCacheKey(config: AxiosRequestConfig): string {
  const { method, url, params, data } = config
  return `${method}-${url}-${JSON.stringify(params)}-${JSON.stringify(data)}`
}

/** 请求重试逻辑 */
function retryRequest(instance: AxiosInstance, config: AxiosRequestConfig & { __retryCount?: number }, retryConfig: RetryConfig): Promise<AxiosResponse> {
  config.__retryCount = config.__retryCount || 0
  
  if (config.__retryCount >= retryConfig.retries) {
    return Promise.reject(new Error('请求重试次数已达上限'))
  }
  
  config.__retryCount += 1
  
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(instance(config))
    }, retryConfig.retryDelay)
  })
}

/** 创建请求实例 */
function createInstance() {
  // 创建一个 axios 实例命名为 instance
  const instance = axios.create({
    timeout: 10000, // 增加超时时间
  })
  
  // 请求拦截器
  instance.interceptors.request.use(
    (config) => {
      // 添加请求时间戳
      config.metadata = { startTime: new Date() }
      
      // 为GET请求添加缓存控制
      if (config.method === 'get' && config.cache !== false) {
        const cacheKey = generateCacheKey(config)
        if (requestCache.has(cacheKey)) {
          return Promise.reject({ __CANCEL__: true, cacheKey })
        }
      }
      
      return config
    },
    error => Promise.reject(error)
  )
  // 响应拦截器（可根据具体业务作出相应的调整）
  instance.interceptors.response.use(
    (response) => {
      // 计算请求耗时
      const endTime = new Date()
      const duration = endTime.getTime() - response.config.metadata?.startTime?.getTime()
      
      // 开发环境下打印请求信息
      if (import.meta.env.DEV) {
        console.log(`[API] ${response.config.method?.toUpperCase()} ${response.config.url} - ${duration}ms`)
      }
      
      // 处理缓存
      if (response.config.method === 'get' && response.config.cache !== false) {
        const cacheKey = generateCacheKey(response.config)
        requestCache.delete(cacheKey)
      }
      
      // apiData 是 api 返回的数据
      const apiData = response.data
      // 二进制数据则直接返回
      const responseType = response.request?.responseType
      if (responseType === "blob" || responseType === "arraybuffer") return apiData
      
      // 这个 code 是和后端约定的业务 code
      const code = apiData.code
      // 如果没有 code, 代表这不是项目后端开发的 api
      if (code === undefined) {
        return apiData
      }
      
      switch (code) {
        case 0:
          // 本系统采用 code === 0 来表示没有业务错误
          return apiData.data
        case 401:
          // Token 过期时
          ElMessage.error('登录已过期，请重新登录')
          return logout()
        default:
          // 不是正确的 code
          if (apiData.message) {
            ElMessage.error(apiData.message)
          }
          return Promise.reject(new Error(apiData.message || 'Unknown error'))
      }
    },
    async (error) => {
      // 处理取消的请求（缓存）
      if (error.__CANCEL__) {
        const cachedRequest = requestCache.get(error.cacheKey)
        if (cachedRequest) {
          return cachedRequest
        }
      }
      
      // 网络错误重试
      const retryConfig: RetryConfig = {
        retries: 3,
        retryDelay: 1000,
        retryCondition: (err) => {
          return !err.response || err.response.status >= 500
        }
      }
      
      if (retryConfig.retryCondition && retryConfig.retryCondition(error) && error.config) {
        try {
          return await retryRequest(instance, error.config, retryConfig)
        } catch (retryError) {
          // 重试失败，继续原有错误处理
        }
      }
      
      // status 是 HTTP 状态码
      const status = get(error, "response.status")
      const message = get(error, "response.data.message")
      
      let errorMessage = "网络请求失败"
      
      if (status) {
        switch (status) {
          case 400:
            errorMessage = message || "请求参数错误"
            break
          case 401:
            errorMessage = message || "未授权，请重新登录"
            logout()
            break
          case 403:
            errorMessage = message || "拒绝访问"
            break
          case 404:
            errorMessage = "请求的资源不存在"
            break
          case 408:
            errorMessage = "请求超时"
            break
          case 500:
            errorMessage = "服务器内部错误"
            break
          case 501:
            errorMessage = "服务未实现"
            break
          case 502:
            errorMessage = "网关错误"
            break
          case 503:
            errorMessage = "服务不可用"
            break
          case 504:
            errorMessage = "网关超时"
            break
          case 505:
            errorMessage = "HTTP 版本不受支持"
            break
          default:
            errorMessage = message || `请求失败 (${status})`
        }
      } else if (error.code === 'ECONNABORTED') {
        errorMessage = "请求超时，请检查网络连接"
      } else if (!window.navigator.onLine) {
        errorMessage = "网络连接已断开，请检查网络"
      }
      
      error.message = errorMessage
      ElMessage.error(errorMessage)
      return Promise.reject(error)
    }
  )
  return instance
}

/** 创建请求方法 */
function createRequest(instance: AxiosInstance) {
  return <T>(config: AxiosRequestConfig): Promise<T> => {
    const token = getToken()
    // 默认配置
    const defaultConfig: AxiosRequestConfig = {
      // 接口地址
      baseURL: import.meta.env.VITE_BASE_URL,
      // 请求头
      headers: {
        // 携带 Token
        "Authorization": token ? `Bearer ${token}` : undefined,
        "Content-Type": "application/json"
      },
      // 请求体
      data: {},
      // 请求超时
      timeout: 5000,
      // 跨域请求时是否携带 Cookies
      withCredentials: false
    }
    // 将默认配置 defaultConfig 和传入的自定义配置 config 进行合并成为 mergeConfig
    const mergeConfig = merge(defaultConfig, config)
    return instance(mergeConfig)
  }
}

/** 用于请求的实例 */
const instance = createInstance()

/** 用于请求的方法 */
export const request = createRequest(instance)
